#!/usr/bin/env node

/**
 * Performance Testing Script
 *
 * Comprehensive performance testing for Vision Analyzer MCP Server
 */

const fs = require('fs');
const path = require('path');
const http = require('http');
const { performance } = require('perf_hooks');

class PerformanceTester {
    constructor() {
        this.results = {
            testSuite: 'Vision Analyzer Performance Test',
            timestamp: new Date().toISOString(),
            nodeVersion: process.version,
            platform: process.platform,
            tests: []
        };
        this.baseUrl = `http://localhost:${process.env.PORT || 3000}`;
    }

    /**
     * Run all performance tests
     */
    async runAllTests() {
        console.log('🚀 Starting Vision Analyzer Performance Tests...\n');

        try {
            // Wait a bit for server to be ready
            await this.waitForServer();

            // Run individual test suites
            await this.testApiResponse();
            await this.testConcurrentRequests();
            await this.testMemoryUsage();
            await this.testImageProcessing();
            await this.testProviderPerformance();

            // Generate report
            this.generateReport();

        } catch (error) {
            console.error('❌ Performance test failed:', error.message);
            process.exit(1);
        }
    }

    /**
     * Wait for server to be ready
     */
    async waitForServer(maxRetries = 30) {
        console.log('⏳ Waiting for server to be ready...');

        for (let i = 0; i < maxRetries; i++) {
            try {
                await this.makeRequest('/health');
                console.log('✅ Server is ready\n');
                return;
            } catch (error) {
                if (i === maxRetries - 1) {
                    throw new Error('Server not ready after maximum retries');
                }
                await this.sleep(1000);
            }
        }
    }

    /**
     * Test basic API response times
     */
    async testApiResponse() {
        console.log('📊 Testing API Response Times...');

        const endpoints = [
            '/health',
            '/metrics'
        ];

        for (const endpoint of endpoints) {
            const times = [];
            const iterations = 10;

            for (let i = 0; i < iterations; i++) {
                const startTime = performance.now();
                try {
                    await this.makeRequest(endpoint);
                    const endTime = performance.now();
                    times.push(endTime - startTime);
                } catch (error) {
                    console.error(`   ❌ ${endpoint} failed: ${error.message}`);
                }
            }

            if (times.length > 0) {
                const avg = times.reduce((a, b) => a + b, 0) / times.length;
                const min = Math.min(...times);
                const max = Math.max(...times);
                const p95 = this.percentile(times, 95);

                console.log(`   📈 ${endpoint}:`);
                console.log(`      Average: ${avg.toFixed(2)}ms`);
                console.log(`      Min: ${min.toFixed(2)}ms`);
                console.log(`      Max: ${max.toFixed(2)}ms`);
                console.log(`      95th percentile: ${p95.toFixed(2)}ms`);

                this.results.tests.push({
                    name: `API Response - ${endpoint}`,
                    type: 'api-response',
                    metrics: { avg, min, max, p95, iterations: times.length }
                });
            }
        }

        console.log('');
    }

    /**
     * Test concurrent request handling
     */
    async testConcurrentRequests() {
        console.log('🔄 Testing Concurrent Request Handling...');

        const concurrencyLevels = [1, 5, 10, 20];
        const endpoint = '/health';

        for (const concurrency of concurrencyLevels) {
            console.log(`   📊 Testing ${concurrency} concurrent requests...`);

            const startTime = performance.now();
            const promises = [];

            for (let i = 0; i < concurrency; i++) {
                promises.push(this.makeRequest(endpoint));
            }

            try {
                const results = await Promise.allSettled(promises);
                const endTime = performance.now();
                const totalTime = endTime - startTime;

                const successful = results.filter(r => r.status === 'fulfilled').length;
                const failed = results.filter(r => r.status === 'rejected').length;

                const requestsPerSecond = (successful / totalTime) * 1000;

                console.log(`      ✅ Successful: ${successful}/${concurrency}`);
                console.log(`      ❌ Failed: ${failed}/${concurrency}`);
                console.log(`      ⚡ Total time: ${totalTime.toFixed(2)}ms`);
                console.log(`      📈 Requests/sec: ${requestsPerSecond.toFixed(2)}`);

                this.results.tests.push({
                    name: `Concurrent Requests - ${concurrency}`,
                    type: 'concurrent',
                    metrics: {
                        concurrency,
                        successful,
                        failed,
                        totalTime,
                        requestsPerSecond
                    }
                });

            } catch (error) {
                console.error(`   ❌ Concurrency test failed: ${error.message}`);
            }
        }

        console.log('');
    }

    /**
     * Test memory usage over time
     */
    async testMemoryUsage() {
        console.log('💾 Testing Memory Usage...');

        const initialMemory = process.memoryUsage();
        console.log(`   📊 Initial memory: ${Math.round(initialMemory.heapUsed / 1024 / 1024)}MB`);

        const measurements = [];
        const duration = 30000; // 30 seconds
        const interval = 2000; // Every 2 seconds

        const startTime = Date.now();
        while (Date.now() - startTime < duration) {
            // Make some requests to simulate load
            await this.makeRequest('/health');

            const memUsage = process.memoryUsage();
            measurements.push({
                timestamp: Date.now() - startTime,
                heapUsed: memUsage.heapUsed,
                heapTotal: memUsage.heapTotal,
                external: memUsage.external,
                rss: memUsage.rss
            });

            await this.sleep(interval);
        }

        const finalMemory = process.memoryUsage();
        const memoryIncrease = finalMemory.heapUsed - initialMemory.heapUsed;

        // Calculate statistics
        const heapUsages = measurements.map(m => m.heapUsed);
        const maxHeap = Math.max(...heapUsages);
        const minHeap = Math.min(...heapUsages);
        const avgHeap = heapUsages.reduce((a, b) => a + b, 0) / heapUsages.length;

        console.log(`   📊 Final memory: ${Math.round(finalMemory.heapUsed / 1024 / 1024)}MB`);
        console.log(`   📈 Memory increase: ${Math.round(memoryIncrease / 1024 / 1024)}MB`);
        console.log(`   📊 Max memory: ${Math.round(maxHeap / 1024 / 1024)}MB`);
        console.log(`   📊 Avg memory: ${Math.round(avgHeap / 1024 / 1024)}MB`);

        // Check for memory leaks
        const memoryLeakThreshold = 50 * 1024 * 1024; // 50MB
        const hasMemoryLeak = memoryIncrease > memoryLeakThreshold;

        if (hasMemoryLeak) {
            console.log(`   ⚠️  Possible memory leak detected`);
        } else {
            console.log(`   ✅ Memory usage stable`);
        }

        this.results.tests.push({
            name: 'Memory Usage',
            type: 'memory',
            metrics: {
                initialMemory: initialMemory.heapUsed,
                finalMemory: finalMemory.heapUsed,
                memoryIncrease,
                maxMemory: maxHeap,
                avgMemory: avgHeap,
                hasMemoryLeak,
                duration,
                measurements: measurements.length
            }
        });

        console.log('');
    }

    /**
     * Test image processing performance
     */
    async testImageProcessing() {
        console.log('🖼️  Testing Image Processing Performance...');

        // Create a small test image (base64 encoded 1x1 pixel)
        const testImage = '';

        const prompts = [
            'What do you see in this image?',
            'Analyze the colors in this image',
            'Describe the visual elements'
        ];

        for (const prompt of prompts) {
            console.log(`   📊 Testing prompt: "${prompt}"`);

            const times = [];
            const iterations = 3; // Reduced for API costs

            for (let i = 0; i < iterations; i++) {
                const startTime = performance.now();

                try {
                    // Note: This would require MCP client setup in a real test
                    // For now, we'll simulate the timing
                    await this.simulateImageProcessing();
                    const endTime = performance.now();
                    times.push(endTime - startTime);

                } catch (error) {
                    console.error(`      ❌ Request ${i + 1} failed: ${error.message}`);
                }
            }

            if (times.length > 0) {
                const avg = times.reduce((a, b) => a + b, 0) / times.length;
                console.log(`      ⚡ Average time: ${avg.toFixed(2)}ms`);

                this.results.tests.push({
                    name: `Image Processing - ${prompt.substring(0, 30)}...`,
                    type: 'image-processing',
                    metrics: { avg, iterations: times.length }
                });
            }
        }

        console.log('');
    }

    /**
     * Test provider performance
     */
    async testProviderPerformance() {
        console.log('🔌 Testing Provider Performance...');

        // This would test actual provider connectivity and response times
        // For now, we'll simulate the tests

        const providers = ['tencent', 'zai', 'moonshot'];

        for (const provider of providers) {
            console.log(`   📊 Testing ${provider} provider...`);

            try {
                const startTime = performance.now();
                // Simulate provider test
                await this.simulateProviderTest(provider);
                const endTime = performance.now();
                const responseTime = endTime - startTime;

                console.log(`      ✅ ${provider}: ${responseTime.toFixed(2)}ms`);

                this.results.tests.push({
                    name: `Provider Performance - ${provider}`,
                    type: 'provider',
                    metrics: { responseTime }
                });

            } catch (error) {
                console.log(`      ❌ ${provider}: Failed - ${error.message}`);
            }
        }

        console.log('');
    }

    /**
     * Simulate image processing for testing
     */
    async simulateImageProcessing() {
        // Simulate typical image processing time
        const processingTime = 1000 + Math.random() * 2000; // 1-3 seconds
        await this.sleep(processingTime);
    }

    /**
     * Simulate provider test
     */
    async simulateProviderTest(provider) {
        // Simulate provider API call time
        const callTime = 500 + Math.random() * 1500; // 0.5-2 seconds
        await this.sleep(callTime);
    }

    /**
     * Make HTTP request
     */
    async makeRequest(path) {
        return new Promise((resolve, reject) => {
            const url = new URL(path, this.baseUrl);
            const req = http.request({
                hostname: url.hostname,
                port: url.port,
                path: url.pathname,
                method: 'GET',
                timeout: 10000
            }, (res) => {
                let data = '';
                res.on('data', chunk => data += chunk);
                res.on('end', () => {
                    if (res.statusCode >= 200 && res.statusCode < 300) {
                        try {
                            resolve(JSON.parse(data));
                        } catch {
                            resolve(data);
                        }
                    } else {
                        reject(new Error(`HTTP ${res.statusCode}: ${data}`));
                    }
                });
            });

            req.on('error', reject);
            req.on('timeout', () => {
                req.destroy();
                reject(new Error('Request timeout'));
            });

            req.end();
        });
    }

    /**
     * Calculate percentile
     */
    percentile(arr, p) {
        const sorted = arr.slice().sort((a, b) => a - b);
        const index = Math.ceil((p / 100) * sorted.length) - 1;
        return sorted[index];
    }

    /**
     * Sleep utility
     */
    sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    /**
     * Generate performance report
     */
    generateReport() {
        console.log('📋 Performance Test Report');
        console.log('========================\n');

        // Summary
        const totalTests = this.results.tests.length;
        const passedTests = this.results.tests.filter(t => !t.metrics.error).length;
        const failedTests = totalTests - passedTests;

        console.log(`📊 Test Summary:`);
        console.log(`   Total Tests: ${totalTests}`);
        console.log(`   Passed: ${passedTests}`);
        console.log(`   Failed: ${failedTests}`);
        console.log(`   Success Rate: ${((passedTests / totalTests) * 100).toFixed(1)}%\n`);

        // Performance grades
        this.assignPerformanceGrades();

        // Save detailed report
        const reportPath = 'performance-test-report.json';
        fs.writeFileSync(reportPath, JSON.stringify(this.results, null, 2));
        console.log(`📄 Detailed report saved to: ${reportPath}`);

        // Performance recommendations
        this.generateRecommendations();

        console.log('\n✅ Performance testing completed!');
    }

    /**
     * Assign performance grades
     */
    assignPerformanceGrades() {
        console.log('🎯 Performance Grades:');

        this.results.tests.forEach(test => {
            let grade = 'A';
            let comment = 'Excellent';

            if (test.type === 'api-response') {
                if (test.metrics.avg > 1000) {
                    grade = 'C';
                    comment = 'Needs improvement';
                } else if (test.metrics.avg > 500) {
                    grade = 'B';
                    comment = 'Good';
                }
            } else if (test.type === 'concurrent') {
                if (test.metrics.requestsPerSecond < 10) {
                    grade = 'C';
                    comment = 'Low throughput';
                } else if (test.metrics.requestsPerSecond < 50) {
                    grade = 'B';
                    comment = 'Moderate throughput';
                }
            } else if (test.type === 'memory') {
                if (test.metrics.hasMemoryLeak) {
                    grade = 'F';
                    comment = 'Memory leak detected';
                } else if (test.metrics.memoryIncrease > 100 * 1024 * 1024) {
                    grade = 'C';
                    comment = 'High memory usage';
                }
            }

            test.grade = grade;
            console.log(`   ${test.name}: Grade ${grade} - ${comment}`);
        });

        console.log('');
    }

    /**
     * Generate performance recommendations
     */
    generateRecommendations() {
        console.log('💡 Performance Recommendations:');

        const recommendations = [];

        // Analyze test results and generate recommendations
        const apiTests = this.results.tests.filter(t => t.type === 'api-response');
        const slowApis = apiTests.filter(t => t.metrics.avg > 500);
        if (slowApis.length > 0) {
            recommendations.push('Consider optimizing slow API endpoints');
        }

        const concurrentTest = this.results.tests.find(t => t.type === 'concurrent' && t.metrics.concurrency === 20);
        if (concurrentTest && concurrentTest.metrics.failed > 0) {
            recommendations.push('Improve error handling for high concurrency scenarios');
        }

        const memoryTest = this.results.tests.find(t => t.type === 'memory');
        if (memoryTest && memoryTest.metrics.hasMemoryLeak) {
            recommendations.push('Investigate and fix memory leaks');
        }

        if (recommendations.length === 0) {
            console.log('   ✅ Performance looks good! No major issues detected.');
        } else {
            recommendations.forEach((rec, index) => {
                console.log(`   ${index + 1}. ${rec}`);
            });
        }

        console.log('');
    }
}

// Run performance tests if called directly
if (require.main === module) {
    const tester = new PerformanceTester();
    tester.runAllTests().catch(error => {
        console.error('Performance test failed:', error);
        process.exit(1);
    });
}

module.exports = PerformanceTester;